<template>
  <div :style="{ 'transform': `scale(${showChart ? 1 : 0})` }" class="ai-chart">
    <div class="chart-header">
      <span>大模型智能答疑</span>
      <i class="el-icon-close" @click="closeChart" />
    </div>
    <!-- ai聊天内容显示区域 -->
    <div ref="chartCont" class="chart-cont">
      <div v-for="(item, index) in chartList" :key="index" class="chart-item">
        <template v-if="item.type === 'user'">
          <div style=" width: 100%; justify-content: end; display: flex;align-items: center; gap: 12px;">
            <div class="chart-me new-line">{{ item.text }}</div>
            <div style="width: 32px;">
              <svg t="1744781029334" class="icon" viewBox="0 0 1024 1024" version="1.1"
                xmlns="http://www.w3.org/2000/svg" p-id="28992" xmlns:xlink="http://www.w3.org/1999/xlink" width="32"
                height="32">
                <path d=" M795.9 856.4L513
            910.1V586.3l282.9-53.7z" fill="#4377fb" p-id="28993"></path>
                <path
                  d="M513.8 950.7h-0.9c-7.7 0.2-14.8-2.1-20.3-6.5-91.1-45.6-181-70-267.3-72.6-1.8-0.1-3.6-0.3-5.3-0.8-16.3-4.5-28.1-18.7-28.1-33.7V529.8c0-8.9 4.1-17.1 11.1-22.5 6.9-5.3 16-7.3 25.3-5.6 99.4-0.6 195.1 25.2 284.6 76.9 89.5-51.6 185.2-77.5 284.6-76.9 9.3-1.6 18.3 0.4 25.3 5.6 7.1 5.4 11.1 13.6 11.1 22.5V837c0 15-11.8 29.2-28.1 33.7-1.7 0.5-3.5 0.8-5.3 0.8-86.3 2.6-176.2 27.1-267.3 72.6-5.3 4.3-12 6.6-19.4 6.6zM236.9 827c89.8 4.4 182.5 30.3 276 77.1 93.5-46.8 186.3-72.7 276-77.1V546.7c-91.2 0.9-179.1 26.2-261.3 75.4-1.6 0.9-3.3 1.7-5.1 2.2-6.1 2.9-13.1 2.9-19.2 0-1.8-0.5-3.5-1.3-5.1-2.2-82.2-49.2-170.1-74.5-261.3-75.4V827z"
                  fill="#4377fb" p-id="28994"></path>
                <path
                  d="M189.3 734.5h-14.1c-9.3 0-16.9-7.6-16.9-16.9v-63.3c0-9.3 7.6-16.9 16.9-16.9h14.1c9.3 0 16.9 7.6 16.9 16.9v63.3c0 9.3-7.6 16.9-16.9 16.9zM850.7 734.5h-14.5c-9.3 0-16.9-7.6-16.9-16.9v-63.3c0-9.3 7.6-16.9 16.9-16.9h14.5c9.3 0 16.9 7.6 16.9 16.9v63.3c0 9.3-7.6 16.9-16.9 16.9z"
                  fill="#4377fb" p-id="28995"></path>
                <path
                  d="M670.4 324.1c-2.8 45-72.2 125.2-141.3 122.2-108.4-4.7-145.1-8.2-140.3-116.6 4.7-108.4 96.8-197.1 204.9-187.8 65.5 5.5 81.3 108.3 76.7 182.2z"
                  fill="#4377fb" p-id="28996"></path>
                <path
                  d="M664.7 466.6l-46.9-27.9c49.2-33.1 81.7-89.4 81.7-153.1 0-101.7-82.7-184.4-184.4-184.4s-184.4 82.7-184.4 184.4c0 61.6 30.4 116.3 77 149.8l-50.5 26.8c-10.8 6.1-14.7 19.8-8.7 30.6s19.8 14.7 30.6 8.7l76.4-41.3c18.7 6.4 38.7 9.9 59.5 9.9 18.3 0 36-2.7 52.7-7.7l75.1 43.5c3.5 1.9 7.2 2.8 10.9 2.8 7.9 0 15.6-4.2 19.7-11.6 6.1-10.8 2.1-24.5-8.7-30.5z m-151-35.1c-80.4 0-145.6-65.2-145.6-145.6s65.2-145.6 145.6-145.6c41.7 0 79.4 17.6 105.9 45.7 24.8 25.9 40 61.1 40 99.7 0 20.1-4.2 39.3-11.6 56.7-22.1 52.3-73.9 89.1-134.3 89.1z"
                  fill="#4377fb" p-id="28997"></path>
              </svg>
            </div>
          </div>
        </template>
        <template v-else>
          <div style="display: flex;align-items: center;gap: 12px;">
            <div style="width: 32px;">
              <svg t="1744778681571" class="icon" viewBox="0 0 1024 1024" version="1.1"
                xmlns="http://www.w3.org/2000/svg" p-id="24955" width="32" height="32"
                xmlns:xlink="http://www.w3.org/1999/xlink">
                <path
                  d="M83.029095 470.484703c42.034795 0 76.107628 34.072834 76.107628 76.107628 0 31.512846-19.155906 58.556714-46.457773 70.115658v100.549509c0 16.527919 9.169955 33.059839 23.837883 40.406802L490.463105 961.545305a44.704782 44.704782 0 0 0 47.684767 0l353.947272-203.876005c14.667928-9.188955 23.837884-23.883883 23.837884-40.410802v-77.145624c0-14.695928 12.836937-27.550865 27.509865-27.550865 14.666928 0 25.672875 11.019946 25.672875 27.549865v77.146624c0 36.733821-20.169902 69.792659-51.346749 88.159569l-352.111281 203.881005c-16.508919 9.183955-34.84783 14.695928-51.351749 14.695928-18.33991 0-34.84383-3.671982-51.35175-12.854937L109.011968 805.418067c-31.180848-18.36691-51.351749-51.429749-51.35175-88.159569v-98.894517c-29.557856-10.446949-50.739752-38.639811-50.739752-71.77565 0-42.034795 34.073834-76.108628 76.108629-76.108628z m464.201733-314.088467l1.511993 0.856996L800.442592 302.458523c21.568895 14.39093 36.102824 37.564817 36.72082 62.657694l0.023 1.678992v290.408582c0 26.996868-12.32994 50.469754-35.267828 63.51469l-1.475992 0.815996-251.698771 145.204291c-21.540895 12.578939-50.111755 12.864937-71.975649 0.862996l-1.512993-0.857996-251.698771-143.3693c-21.568895-14.38593-36.102824-37.563817-36.72082-62.656694l-0.023-1.669992V366.795209c0-26.992868 14.090931-50.461754 35.377827-63.51569l1.365993-0.811996 251.698771-145.204291c21.540895-12.577939 50.111755-12.863937 71.975649-0.861996z m-65.57268 159.106223h-84.350588L274.45116 654.986802h78.847615l25.673875-77.490622h122.8564l25.672874 77.490622h78.847615L481.658148 315.502459z m240.213827-1.845991H650.359324v339.486343h71.513651V313.656468zM565.657738 13.779933l353.942272 203.876004c31.179848 18.37191 51.351749 51.429749 51.351749 88.16557l0.004 87.777571c27.122868 11.632943 46.121775 38.583812 46.121775 69.967658 0 42.034795-34.072834 76.107628-76.107629 76.107629s-76.107628-34.072834-76.107628-76.107629c0-32.573841 20.4659-60.369705 49.23976-71.227652v-84.677586c0-16.531919-9.169955-33.062839-23.842884-40.410803L538.147872 61.5287a44.704782 44.704782 0 0 0-47.684767 0l-353.946272 203.889004c-14.667928 9.179955-23.837884 23.875883-23.837883 40.406803v69.797659c0 14.695928-12.836937 27.550865-27.509866 27.550865-14.667928 0-27.509866-12.854937-27.509866-27.549865v-69.802659c0-36.734821 20.171902-69.793659 51.35275-88.16557L462.954239 13.779933c31.179848-18.37191 71.522651-18.37191 102.703499 0z m-122.510402 358.918247c1.835991 12.915937 3.671982 22.140892 5.502973 29.521856l38.509812 118.081423h-89.852561l38.509812-118.081423c1.831991-9.225955 5.498973-20.295901 5.498973-29.520856z"
                  fill="#4377fb" p-id="24956"></path>
              </svg>
            </div>
            <!-- <div class="chart-ai new-line">{{ item.text }}</div> -->
             <div class="chart-ai new-line"><span v-html="item.text"></span>></div>
          </div>
        </template>
      </div>
    </div>
    <!-- 输入区域 -->
    <div class="chart-input">
      <div class="recording-status" v-if="isRecording">
        <div class="recording-wave"></div>
        <span class="recording-text">正在录音...</span>
      </div>
      <el-input v-model="chartCont" type="textarea" :rows="4" placeholder="请输入问题，按Enter发送（注意~要使用燕山大学校内校园网哦）"
        @keyup.enter.native="sendMessage" />
      <div style="display: flex; width: auto; justify-content:end ; margin: 12px;">
        <!-- 语音输入按钮 -->
        <!-- <el-button :class="{ 'recording': isRecording }" type="primary" class="voice-btn" @click="toggleRecording">
          <i :class="isRecording ? 'el-icon-microphone' : 'el-icon-mic'" />
          <span class="voice-btn-text">{{ isRecording ? '结束录音' : '开始录音' }}</span>
        </el-button> -->

        <!-- 发送按钮 -->
        <el-button type="primary" class="send-btn" :loading="chartLoading" @click="sendMessage">
          <i class="el-icon-s-promotion" />
        </el-button>
      </div>

    </div>
  </div>
</template>

<script>

import { mapGetters } from 'vuex'
import { sendQues } from '@/api/course/aiChart'
import { post } from '@/utils/request'
import axios from 'axios'
import { mapState } from 'vuex'
// 新增：引入KaTeX核心库和样式（无扩展，避免路径错误）
import katex from 'katex'
import 'katex/dist/katex.min.css'

export default {
  props: {
    showChart: {
      type: Boolean,
      default: false
    },
    videoRef: {
      type: Object,
      default: null
    },
    courseId: {
      type: String,
      default: ''
    },
  },
  data() {
    return {
      chartCont: '',
      chartList: [],
      showTip: false,
      chartLoading: false,

      // 语音相关
      isRecording: false,
      statusText: '',
      transcriptionResult: '',
      questionAnswers: [],
      showConfirmation: false,
      lastTranscriptionResult: '',
      timerId: null,
      mediaRecorder: null,
      audioChunks: [],
      recognition: null,
      transcriptionText: '',
      lastTranscriptionText: '',
      audio: null,
      // 用于跟踪当前AI回复的索引
      currentAiResponseIndex: -1
    }
  },
  computed: {
    ...mapGetters([
      'avatar'
    ]),
    ...mapState({})
  },
  mounted() {
    this.scrollCont()

    // 初始化语音识别
    if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
      const SpeechRecognition = window.webkitSpeechRecognition || window.SpeechRecognition
      this.recognition = new SpeechRecognition()
      this.recognition.continuous = true
      this.recognition.interimResults = true
      this.recognition.lang = 'zh-CN'

      this.recognition.onresult = (event) => {
        // 处理结果
        let interimTranscript = '';
        let finalTranscript = '';

        for (let i = event.resultIndex; i < event.results.length; ++i) {
          if (event.results[i].isFinal) {
            finalTranscript += event.results[i][0].transcript;
          } else {
            interimTranscript += event.results[i][0].transcript;
          }
        }

        // 更新最终识别结果
        if (finalTranscript) {
          // 保存最后一个完整的识别结果
          this.lastTranscriptionText = finalTranscript;
          // 将结果追加到输入框
          this.chartCont += finalTranscript;
        }

        // 更新临时识别结果显示
        this.transcriptionText = interimTranscript;
      }

      this.recognition.onerror = (event) => {
        console.error('语音识别错误:', event.error)
        this.$message.error('语音识别出错，请重试')
        this.isRecording = false
      }

      this.recognition.onend = () => {
        if (this.isRecording) {
          // 如果还在录音状态，则重新启动识别
          this.recognition.start()
        } else if (this.transcriptionText) {
          // 如果有临时识别的文字，在结束时也加入输入框
          this.chartCont += this.transcriptionText;
          this.transcriptionText = '';
        }
      }
    } else {
      this.$message.warning('您的浏览器不支持语音识别功能')
    }
  },
  methods: {
    // 新增：KaTeX公式渲染方法（适配先处理多行公式，再适配常见语法）////////////////////////////////////////////
    renderKaTeX(text) {
      if (!text) return '';

      // 步骤1：确保引入amsmath扩展（全局配置，需在引入KaTeX后执行）
      if (window.katex && !window.katex.amsmathLoaded) {
        window.katex.use(window.amsmath);
        window.katex.amsmathLoaded = true; // 避免重复加载
      }

      // 步骤2：处理多行公式的换行符（统一为\n，避免解析问题）
      let processedText = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');

      // 步骤3：保留$$块级公式（包含align*等环境），不拆分多行
      // 先匹配$$块级公式并临时标记，避免被后续单$处理干扰
      const blockMarker = '___KATEX_BLOCK_MARKER___';
      const blockFormulas = [];
      processedText = processedText.replace(/\$\$([\s\S]*?)\$\$/g, (match, content) => {
        blockFormulas.push(content);
        return blockMarker; // 用标记暂代
      });

      // 步骤4：处理单$包裹的行内公式（仅拆分非对齐环境的多行）
      processedText = processedText.replace(/\$([\s\S]*?)\$/g, (match, content) => {
        if (content.includes('\n') && !content.includes('align') && !content.includes('aligned')) {
          // 仅拆分不含对齐环境的多行单$公式
          return content
            .split('\n')
            .map(line => line.trim())
            .filter(line => line)
            .map(line => `$${line}$`)
            .join('\n');
        }
        return match;
      });

      // 步骤5：恢复$$块级公式并渲染
      processedText = processedText.replace(new RegExp(blockMarker, 'g'), () => {
        const content = blockFormulas.shift();
        return `$$${content}$$`;
      });

      // 步骤6：渲染所有公式（适配align*环境）
      const katexRegex = /(\$\$[\s\S]*?\$\$)|(\$[^\$]+\$)/g;
      return processedText.replace(katexRegex, (match) => {
        try {
          const isBlock = match.startsWith('$$') && match.endsWith('$$');
          let formula = isBlock 
            ? match.slice(2, -2).trim() 
            : match.slice(1, -1).trim();
          
          // 语法适配（增强对齐环境支持）
          formula = formula
            .replace(/\\mathscr\{(.*?)\}/g, '\\mathcal{$1}')
            .replace(/\\boldsymbol\{(.*?)\}/g, '\\mathbf{$1}')
            .replace(/k₁/g, 'k1')
            .replace(/k₂/g, 'k2')
            .replace(/\\sum/g, isBlock ? '\\displaystyle\\sum' : '\\sum')
            // 修复align环境中的换行符（确保\\正确解析）
            .replace(/\\\\+/g, '\\\\'); // 合并连续换行符，避免解析错误
          
          return katex.renderToString(formula, {
            displayMode: isBlock,
            throwOnError: false,
            errorColor: '#ff4d4f',
            strict: 'ignore',
            extensions: ['amsmath'] // 显式指定扩展
          });
        } catch (error) {
          console.error('公式渲染失败:', error);
          return match;
        }
      });
    },
    /////////////////////////////////////////////////////////////////////
    closeChart() {
      // 停止播放语音
      if (this.audio) {
        this.audio.pause()
        this.audio = null
      }
      this.$emit('closeChart', false)
    },
    scrollCont() {
      const chartCont = this.$refs.chartCont;
      if (chartCont) {
        chartCont.scrollTop = chartCont.scrollHeight;
      }
    },
    // 发送消息
    async sendMessage() {
      if (!this.chartCont.trim()) {
        this.showTip = true
        setTimeout(() => {
          this.showTip = false
        }, 2000)
        return
      }

      const question = this.chartCont.trim()
      this.chartList.push({
        type: 'user',
        text: question
      })
      this.$nextTick(() => {
        this.scrollCont(); // 滚动到底部
      });
      this.chartCont = ''
      this.chartLoading = true

      try {
        // 添加一个空的AI回复占位
        this.currentAiResponseIndex = this.chartList.length
        this.chartList.push({
          type: 'ai',
          text: ''
        })
        this.$nextTick(() => {
          this.scrollCont();
        });

        // 使用Fetch API实现流式接收
        const response = await fetch('http://10.21.22.122:9025/retrieve_knowledge_stream', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            query: question,
            course_id: this.courseId
          })
        });

        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }

        // 获取响应流的阅读器
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let fullResponseText = '';
        let audioAnswer = null;

        // 循环读取流数据
        while (true) {
          const { done, value } = await reader.read();
          
          if (done) {
            break; // 流结束
          }
          
          // 解码新接收的部分
          const chunk = decoder.decode(value, { stream: true });
          //解码字符
          // console.log("chunk:"+chunk);
          
          
          try {
            // 解析JSON数据
            const lines = chunk.split('\n').filter(line => line.trim() !== '');
            console.log(lines)
            // 只处理text_answer内容

            if (lines) {
              lines.forEach(line => {
                console.log("line:" + line);
                // 判断line是否以"{"开头且以"}"结尾
                if (line.startsWith("{") && line.endsWith("}")) {
                  try {
                    const data = JSON.parse(line);
                    // 累加text_answer
                    fullResponseText += data.text_answer || "";                                    
                    // 当done为true时播放语音
                    if(data.audio_answer){
                      // fullResponseText=this.renderKaTeX(fullResponseText)
                      audioAnswer+=data.audio_answer
                      console.log("full_audioAnswer:"+audioAnswer)
                    }
                    if (data.done && data.done === true && audioAnswer) {
                      this.playAudioResponse(audioAnswer);
                    }
                  } catch (e) {
                    console.error("解析JSON失败:", e);
                  }
                }
                // 判断line是否以"{"开头且不以"}"结尾
                else if (line.startsWith("{") && !line.endsWith("}")) {
                  // fullResponseText=this.renderKaTeX(fullResponseText)
                  // 提取"audio_answer": "后的部分
                  const audioStartIndex = line.indexOf('"audio_answer": "');
                  if (audioStartIndex !== -1) {
                    const audioContent = line.substring(audioStartIndex + '"audio_answer": "'.length);
                    audioAnswer += audioContent;
                  }
                }
                // 判断line是否不以"{"开头且以"}"结尾
                else if (!line.startsWith("{") && line.endsWith("}")) {
                  // 提取, "code"前的部分
                  const codeIndex = line.indexOf('", "code"');
                  if (codeIndex !== -1) {
                    const audioContent = line.substring(0, codeIndex);
                    audioAnswer += audioContent;
                  }
                  console.log("audioAnswer:"+audioAnswer)
                  // 播放语音
                  this.playAudioResponse(audioAnswer);
                }
                else{
                  audioAnswer += line;
                }
              }); 
              // 更新UI显示
              fullResponseText=this.renderKaTeX(fullResponseText)
              this.chartList[this.currentAiResponseIndex].text = fullResponseText;
              this.$nextTick(() => {
                this.scrollCont(); // 滚动到底部
              });
            }
          } catch (error) {
            console.error('解析响应数据失败:', error, '原始数据:', chunk);
          }
        }
      } catch (error) {
        console.error('发送消息失败:', error)
        this.$message.error('发送消息失败，请重试')
        // 出错时更新AI回复状态
        if (this.currentAiResponseIndex !== -1 && this.currentAiResponseIndex < this.chartList.length) {
          this.chartList[this.currentAiResponseIndex].text = '获取回答失败，请重试'
        }
      } finally {
        this.chartLoading = false
        this.currentAiResponseIndex = -1 // 重置索引
        this.$nextTick(() => {
          this.scrollCont()
        })
      }
    },

    // 切换录音状态
    async toggleRecording() {
      if (this.isRecording) {
        // 停止录音
        if (!this.recognition) return

        this.isRecording = false
        this.recognition.stop()

        // 如果有临时识别的文字，加入到输入框
        if (this.transcriptionText) {
          this.chartCont += this.transcriptionText;
          this.transcriptionText = '';
        }
      } else {
        // 开始录音
        if (!this.recognition) {
          this.$message.warning('您的浏览器不支持语音识别功能')
          return
        }

        try {
          await navigator.mediaDevices.getUserMedia({ audio: true })
          this.isRecording = true
          this.transcriptionText = ''
          this.recognition.start()
        } catch (error) {
          console.error('开启录音失败:', error)
          this.$message.error('无法访问麦克风，请检查权限设置')
        }
      }
    },

    // 播放音频响应
    playAudioResponse(base64Audio) {
      if (this.audio) {
        this.audio.pause()
      }
      
      // 处理可能的音频格式问题
      try {
        // 移除可能的前缀
        const cleanBase64 = base64Audio.replace(/^[^,]+,/, '');
        // 创建音频对象并播放
        this.audio = new Audio(`data:audio/mpeg;base64,${cleanBase64}`);
        this.audio.play().catch(error => {
          console.error('播放音频失败:', error);
          this.$message.warning('音频播放失败，请检查浏览器设置');
        });
      } catch (error) {
        console.error('处理音频数据失败:', error);
        this.$message.error('处理音频时发生错误');
      }
    }
  },
  beforeDestroy() {
    // 组件销毁时停止语音识别
    if (this.recognition) {
      this.recognition.stop()
    }
    
    // 停止播放音频
    if (this.audio) {
      this.audio.pause();
      this.audio = null;
    }
  }
}
</script>

<style scoped lang="scss">
/* 变量定义 */
$primary-color: #007BFF;
$danger-color: #ff4d4f;
$text-color: #333;
$text-color-light: #666;
$text-color-lighter: #999;
$border-color: #e0e0e0;
$bg-color: #fff;
$bg-color-light: #f4f4f9;
$bg-color-lighter: #f9f9f9;
$border-radius: 10px;
$border-radius-sm: 8px;
$border-radius-xs: 4px;
$box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
$box-shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);

.ai-chart {
  width: 30%;
  min-width: 400px;
  max-width: 700px;
  height: 647px;
  position: fixed;
  bottom: 70px;
  right: 30px;
  background: linear-gradient(195deg, #8aabfe, 20%, #fff);
  border: 1px solid #d6e1fef0;
  border-radius: 12px;
  padding-top: 15px;
  box-sizing: border-box;
  z-index: 99;
  transition: 300ms ease-out transform;

  .chart-header {
    height: 20px;
    display: flex;
    justify-content: space-between;
    font-weight: bold;
    padding: 0 15px;

    .el-icon-close {
      font-size: 18px;
      cursor: pointer;
      color: #fff;
      font-weight: bold;

      &:hover {
        transform: scale(1.2);
      }
    }
  }

  /* 保留原DhChart的部分样式 */
  .chart-cont {
    width: 100%;

    height: calc(100% - 200px);
    font-size: 14px;
    background: rgba(255, 255, 255, 0.5);
    margin-top: 10px;
    overflow-y: scroll;
    padding: 15px 15px 0 15px;
    box-sizing: border-box;

    -ms-overflow-style: none;
    /* 适用于 Internet Explorer 和旧版 Edge */
    scrollbar-width: none;
    /* 适用于 Firefox */

    .avatar {
      width: 30px;
      height: 30px;
      border-radius: 50%;
      object-fit: cover;
      margin-right: 15px;
    }

    .chart-item {
      width: 100%;
      margin-bottom: 15px;
      display: flex;
    }

    .new-line {
      line-height: 28px;
      white-space: pre-wrap;
      padding: 5px 10px;
      border-radius: 6px;
    }

    .chart-me {
      // font-weight: bold;
      background: rgba(255, 255, 255, 0.3);
    }

    .chart-ai {
      width: calc(100% - 45px);
      background: #fff;
    }
  }

  .chart-input {
    width: 100%;
    box-sizing: border-box;
    position: absolute;
    left: 0;
    bottom: 0;
    border-top: 1px solid #90aef9;
    background: #fff;
    border-bottom-left-radius: 12px;
    border-bottom-right-radius: 12px;

    .recording-status {
      position: absolute;
      top: -60px;
      right: 65px; // 将位置改到右下角的录音按钮上方
      background: rgba(0, 0, 0, 0.7);
      padding: 8px 16px;
      border-radius: 20px;
      color: #fff;
      display: flex;
      align-items: center;
      gap: 8px;
      z-index: 100;

      .recording-wave {
        width: 12px;
        height: 12px;
        background: #f56c6c;
        border-radius: 50%;
        animation: wave 1s infinite;
      }

      .recording-text {
        font-size: 14px;
      }

      .live-text {
        position: absolute;
        top: -30px;
        left: 50%;
        transform: translateX(-50%);
        background: rgba(0, 0, 0, 0.7);
        padding: 4px 8px;
        border-radius: 4px;
        font-size: 12px;
        white-space: nowrap;
        max-width: 300px;
        overflow: hidden;
        text-overflow: ellipsis;
      }
    }

    .voice-btn {
      bottom: 10px;
      right: 65px;
      padding: 7px 12px;
      display: flex;
      align-items: center;
      gap: 5px;
      transition: all 0.3s;

      &.recording {
        background-color: #f56c6c;
        border-color: #f56c6c;
        animation: pulse 1.5s infinite;
      }

      .voice-btn-text {
        font-size: 12px;
      }
    }

    .send-btn {}

    ::v-deep .el-textarea__inner {
      border: none;
      padding-top: 12px;
      resize: none;
      -ms-overflow-style: none;
      /* 适用于 Internet Explorer 和旧版 Edge */
      scrollbar-width: none;
      /* 适用于 Firefox */

      &:focus {
        box-shadow: none;
      }
    }
  }
}

@keyframes wave {
  0% {
    box-shadow: 0 0 0 0 rgba(245, 108, 108, 0.4);
  }

  70% {
    box-shadow: 0 0 0 10px rgba(245, 108, 108, 0);
  }

  100% {
    box-shadow: 0 0 0 0 rgba(245, 108, 108, 0);
  }
}

@keyframes pulse {
  0% {
    transform: scale(1);
  }

  50% {
    transform: scale(1.05);
  }

  100% {
    transform: scale(1);
  }
}
</style>
